%%% File    : db_sup.erl
%%% Author  :  <>
%%% Description : 
%%% Created : 23 Mar 2011 by  <>

-module(db_sup).

-behaviour(gen_server).

-export([start_link/0]).

-export([start_db/1, stop_db/1]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	terminate/2, code_change/3]).

-define(SERVER, ?MODULE).

-record(state, {db}).

%%----------
start_db(Node) when is_atom(Node) ->
    gen_server:call({global, ?SERVER}, {start_db, Node});
start_db(_) ->
    {error, badarg}.

stop_db(Node) when is_atom(Node) ->
    gen_server:call({global, ?SERVER}, {stop_db, Node});
stop_db(_) ->
    {error, badarg}.

start_link() ->
    gen_server:start_link({global, ?SERVER}, ?MODULE, [], []).

%%--------
init([]) ->
    process_flag(trap_exit, true),
    pg2:create(db),
    {ok, #state{db=[]}}.%%{node, pid}

handle_call({start_db, Node}, _From, S) ->
    {Res, Ns} = do_start(Node, S),
    {reply, Res, Ns};

handle_call({stop_db, Node}, _From, S) ->
    {Res, Ns} = do_stop(Node, S),
    {reply, Res, Ns};

handle_call(_Req, _From, S) ->
    {reply, ok, S}.

handle_cast({redirect, Node, Pid}, S=#state{db=Db}) ->
    link(Pid),
    {noreply, S#state{db=[{Node, Pid}|Db]}};
handle_cast(_Msg, S) ->
    {noreply, S}.

handle_info({'EXIT', _, normal}, S) ->
    {noreply, S};

handle_info({'EXIT', Pid, _Reason}, S=#state{db=Db}) ->
    case lists:keysearch(Pid, 2, Db) of
	false ->
	    {noreply, S};
	{value, {Node, Pid}} ->
	    do_restart(Node, S)
    end;

handle_info(_Info, S) ->
    {noreply, S}.

terminate(_Reason, _S) -> 
    ok.

code_change(_Oldvsn, S, _Extra) ->
    {ok, S}.

%%------
do_start(Node, S=#state{db=Db}) ->
    case lists:keysearch(Node, 1, Db) of
	{value, {_Node, _Pid}} ->
	    {{error, exist}, S};
	false ->
	    Res = rpc:call(Node, db_server, start, [self(), connect_db(S)]),
	    case Res of
		{ok, Pid} ->
		    link(Pid),
		    pg2:join(db, Pid),
		    {ok, S#state{db=[{Node, Pid}|Db]}};
		Err ->
		    {{error, Err}, S}
            end
    end.

do_stop(Node, S=#state{db=Db}) ->
    case lists:keysearch(Node, 1, Db) of
	false ->
	    {{error, noexist}, S};
	{value, {Node, Pid}} ->
	    do_exit(Pid),
	    {ok, S#state{db=lists:keydelete(Node, 1, Db)}}
    end.

do_exit(Pid) ->
    gen_server:cast(Pid, {do_exit, self()}).
 		    
do_restart(Node, S=#state{db=Db}) ->
    Res = rpc:call(Node, db_server, start, [self(), connect_db(S)]),
    case Res of
	{ok, Pid} ->
	    link(Pid),
	    pg2:join(db, Pid),
	    {noreply, 
	     S#state{db=[{Node, Pid}|lists:keydelete(Node, 1, Db)]}};
	_Err ->
	    {noreply, S#state{db=lists:keydelete(Node, 1, Db)}}
    end.

connect_db(#state{db=Db}) ->
    connect_db(Db, []).

connect_db([], Nodes) ->
    Nodes;
connect_db([{Node, _}|T], Nodes) ->
    connect_db(T, [Node|Nodes]).
